package recorder

import (
	"fmt"
	"sort"
	"sync"
	"time"
)

type Source struct {
	mu             sync.Mutex
	id             string
	scene          string
	segment        *Segment
	segments       []*Segment
	nextSegmentSeq int
	idleStartTime  time.Time
}

func NewSource(scene string, name string) *Source {
	return &Source{
		id:             name,
		scene:          scene,
		segment:        nil,
		segments:       make([]*Segment, 0),
		nextSegmentSeq: 0,
		idleStartTime:  time.Now(),
	}
}

func (s *Source) AddSegment(segment *Segment) string {
	s.mu.Lock()
	id := fmt.Sprintf("%04d", s.nextSegmentSeq)
	segment.id = id
	s.nextSegmentSeq += 1

	if len(s.segments) != 0 {
		l := s.lastSegment()
		l.Close()
	}
	s.segments = append(s.segments, segment)

	s.idleStartTime = time.Time{}
	s.mu.Unlock()
	return id
}

func (s *Source) AddSegment_2(segment *Segment) string {
	s.mu.Lock()
	id := fmt.Sprintf("%04d", s.nextSegmentSeq)
	segment.id = id
	s.nextSegmentSeq += 1

	s.segment = segment

	s.idleStartTime = time.Time{}
	s.mu.Unlock()
	return id
}

func (s *Source) lastSegment() *Segment {
	l := len(s.segments)
	return s.segments[l-1]
}

func (s *Source) CloseSegment(segmentId string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if len(s.segments) == 0 {
		return
	}

	lastSegment := s.segments[len(s.segments)-1]
	if lastSegment.id == segmentId {
		lastSegment.Close()
		s.idleStartTime = time.Now()
	}
}

func (s *Source) IsLive() bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	if !s.idleStartTime.IsZero() {
		log.Tracef("scene '%s' source '%s' idle", s.scene, s.id)
		return false
	}

	l := len(s.segments)

	if l == 0 {
		log.Tracef("scene '%s' source '%s' empty", s.scene, s.id)
		return false
	}

	lastSegmentLive := s.lastSegment().IsLive()

	if !lastSegmentLive {
		log.Tracef("scene '%s' source '%s' last segment dead", s.scene, s.id)
		s.idleStartTime = time.Now()
	}
	return lastSegmentLive
}

func (s *Source) IdleTime() time.Duration {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.idleStartTime.IsZero() {
		return time.Duration(0)
	}
	idleTime := time.Since(s.idleStartTime)
	return idleTime
}

func (s *Source) ToJob() *JobSource {
	s.mu.Lock()
	js := &JobSource{
		Name:     s.id,
		Segments: make([]*JobSegment, 0),
	}

	segmentIdList := make([]int, 0)
	for segmentId := range s.segments {
		segmentIdList = append(segmentIdList, segmentId)
	}
	sort.Ints(segmentIdList)

	for _, segmentId := range segmentIdList {
		segment := s.segments[segmentId]
		js.Segments = append(js.Segments, segment.ToJob())
	}

	s.mu.Unlock()

	return js
}

func (s *Source) ToJob_2() *JobSource_2 {
	s.mu.Lock()
	defer s.mu.Unlock()

	js := &JobSource_2{
		Name:    s.id,
		Segment: s.segment.ToJob(),
	}
	return js
}
